"""
模拟交易所

用于模拟盘交易和回测的虚拟交易所实现
"""

from typing import Dict, List, Optional, Any
from datetime import datetime
import logging
import random
import math
from ..exchanges.base_exchange import BaseExchange


class MockExchange(BaseExchange):
    """模拟交易所"""
    
    def __init__(self, initial_balance: Optional[Dict[str, float]] = None):
        """
        初始化模拟交易所
        
        Args:
            initial_balance: 初始余额
        """
        config = {'name': 'mock', 'testnet': True}
        super().__init__(config)
        
        self.logger = logging.getLogger(__name__)
        
        # 账户余额
        if initial_balance is None:
            initial_balance = {'USDT': 10000.0, 'BTC': 0.0}
        self.balances = initial_balance.copy()
        
        # 当前价格和价格历史
        self.current_price = {}
        self.price_history = {}
        
        # 市场深度模拟
        self.order_book = {}
        
        # 滑点和手续费设置
        self.slippage_rate = 0.0001  # 0.01% 滑点
        self.fee_rate = 0.001        # 0.1% 手续费
        
        # 订单簿
        self.orders = {}
        self.order_history = []
        self.next_order_id = 1
        
        # 交易历史
        self.trade_history = []
        
        self.logger.info(f"模拟交易所初始化完成，初始余额: {initial_balance}")

    def set_price(self, symbol: str, price: float) -> None:
        """
        设置当前价格，并更新价格历史
        
        Args:
            symbol: 交易对
            price: 价格
        """
        # 添加价格波动模拟
        if symbol in self.current_price:
            # 基于历史价格添加小幅随机波动
            old_price = self.current_price[symbol]
            volatility = 0.001  # 0.1% 波动率
            noise = random.gauss(0, volatility)
            price = price * (1 + noise)
        
        self.current_price[symbol] = price
        
        # 更新价格历史
        if symbol not in self.price_history:
            self.price_history[symbol] = []
        
        self.price_history[symbol].append({
            'price': price,
            'timestamp': datetime.now()
        })
        
        # 保持最近100个价格点
        if len(self.price_history[symbol]) > 100:
            self.price_history[symbol] = self.price_history[symbol][-100:]
        
        # 更新订单簿
        self._update_order_book(symbol, price)
        
        self.logger.debug(f"设置价格: {symbol} = {price}")
    
    def _update_order_book(self, symbol: str, price: float) -> None:
        """
        更新订单簿，模拟市场深度
        
        Args:
            symbol: 交易对
            price: 当前价格
        """
        # 生成模拟的买卖盘
        spread = price * 0.001  # 0.1% 价差
        
        # 买盘 (bid)
        bids = []
        for i in range(5):
            bid_price = price - spread * (i + 1)
            bid_quantity = random.uniform(0.1, 10.0)
            bids.append([bid_price, bid_quantity])
        
        # 卖盘 (ask)
        asks = []
        for i in range(5):
            ask_price = price + spread * (i + 1)
            ask_quantity = random.uniform(0.1, 10.0)
            asks.append([ask_price, ask_quantity])
        
        self.order_book[symbol] = {
            'bids': bids,
            'asks': asks,
            'timestamp': datetime.now()
        }
    
    def get_price(self, symbol: str) -> float:
        """
        获取当前价格
        
        Args:
            symbol: 交易对
            
        Returns:
            当前价格
        """
        return self.current_price.get(symbol, 0.0)
    
    def get_current_price(self, symbol: str) -> float:
        """
        获取当前价格（别名方法）
        
        Args:
            symbol: 交易对
            
        Returns:
            当前价格
        """
        return self.get_price(symbol)
    
    def get_market_price(self, symbol: str, side: str, quantity: float) -> float:
        """
        获取市场价格，考虑滑点和市场深度
        
        Args:
            symbol: 交易对
            side: 买卖方向
            quantity: 数量
            
        Returns:
            考虑滑点的实际成交价格
        """
        base_price = self.get_price(symbol)
        if base_price <= 0:
            return 0.0
        
        # 基础滑点
        slippage = self.slippage_rate * quantity / 10.0  # 数量越大滑点越大
        
        # 根据订单簿计算更精确的滑点
        if symbol in self.order_book:
            order_book = self.order_book[symbol]
            if side.lower() == 'buy':
                # 买单使用卖盘价格
                asks = order_book['asks']
                if asks:
                    # 简化计算：使用第一档价格加滑点
                    market_price = asks[0][0] * (1 + slippage)
                else:
                    market_price = base_price * (1 + slippage)
            else:
                # 卖单使用买盘价格
                bids = order_book['bids']
                if bids:
                    # 简化计算：使用第一档价格减滑点
                    market_price = bids[0][0] * (1 - slippage)
                else:
                    market_price = base_price * (1 - slippage)
        else:
            # 没有订单簿时使用简单滑点计算
            if side.lower() == 'buy':
                market_price = base_price * (1 + slippage)
            else:
                market_price = base_price * (1 - slippage)
        
        return market_price
    
    # 实现抽象方法
    def get_spot_balance(self) -> Dict[str, Any]:
        """获取现货账户余额"""
        return {
            'balances': [
                {
                    'asset': asset,
                    'free': str(balance),
                    'locked': '0.0'
                }
                for asset, balance in self.balances.items()
            ]
        }
    
    def spot_place_order(self, symbol: str, order_type: str, side: str, 
                        amount: float, price: Optional[float] = None) -> Dict[str, Any]:
        """现货下单"""
        order_id = str(self.next_order_id)
        self.next_order_id += 1
        
        # 使用当前价格或指定价格
        if price is None:
            price = self.get_price(symbol)
        
        order = {
            'order_id': order_id,
            'symbol': symbol,
            'side': side,
            'type': order_type,
            'amount': amount,
            'price': price,
            'status': 'filled',  # 模拟立即成交
            'timestamp': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),  # 统一格式
        }
        
        self.orders[order_id] = order
        self.order_history.append(order)
        
        # 模拟成交
        self._fill_order(order_id, price, amount)
        
        return order
    
    def get_futures_balance(self) -> Dict[str, Any]:
        """获取合约账户余额"""
        return {
            'totalWalletBalance': str(self.balances.get('USDT', 0.0)),
            'totalUnrealizedProfit': '0.0',
            'totalMarginBalance': str(self.balances.get('USDT', 0.0)),
            'totalPositionInitialMargin': '0.0',
            'totalOpenOrderInitialMargin': '0.0',
            'totalCrossWalletBalance': str(self.balances.get('USDT', 0.0)),
            'totalCrossUnPnl': '0.0',
            'availableBalance': str(self.balances.get('USDT', 0.0)),
            'maxWithdrawAmount': str(self.balances.get('USDT', 0.0))
        }
    
    def get_futures_position(self, symbol: Optional[str] = None) -> Dict[str, Any]:
        """获取合约仓位"""
        return {
            'positions': []  # 模拟空仓位
        }
    
    def futures_place_order(self, symbol: str, order_type: str, side: str, 
                           amount: float, leverage: int, 
                           price: Optional[float] = None) -> Dict[str, Any]:
        """合约下单"""
        return self.spot_place_order(symbol, order_type, side, amount, price)
    
    def get_kline(self, symbol: str, period: str, limit: int = 100) -> List[Dict[str, Any]]:
        """获取K线数据"""
        # 模拟K线数据
        current_price = self.get_price(symbol)
        if current_price == 0:
            current_price = 50000.0  # 默认价格
        
        klines = []
        for i in range(limit):
            klines.append({
                'timestamp': int(datetime.now().timestamp() * 1000) - i * 60000,  # 1分钟间隔
                'open': current_price * (1 + (i % 10 - 5) * 0.001),
                'high': current_price * (1 + (i % 10 - 3) * 0.001),
                'low': current_price * (1 + (i % 10 - 7) * 0.001),
                'close': current_price,
                'volume': 100.0 + i * 10
            })
        
        return list(reversed(klines))  # 按时间正序返回
    
    def get_latest_price(self, symbol: str) -> float:
        """获取最新价格"""
        return self.get_price(symbol)
    
    def cancel_order(self, order_id: str, symbol: str) -> Dict[str, Any]:
        """取消订单"""
        if order_id in self.orders:
            self.orders[order_id]['status'] = 'cancelled'
            return {'order_id': order_id, 'status': 'cancelled'}
        return {'error': 'Order not found'}
    
    def get_order_status(self, order_id: str, symbol: str) -> Dict[str, Any]:
        """获取订单状态"""
        return self.orders.get(order_id, {'error': 'Order not found'})
    
    # 原有方法保持不变
    def get_ticker(self, symbol: str) -> Dict[str, Any]:
        """
        获取行情信息
        
        Args:
            symbol: 交易对
            
        Returns:
            行情信息
        """
        price = self.get_price(symbol)
        return {
            'symbol': symbol,
            'price': price,
            'bid': price * 0.999,
            'ask': price * 1.001,
            'volume': 1000.0,
            'timestamp': int(datetime.now().timestamp() * 1000)  # 统一格式
        }
    
    def get_spot_ticker(self, symbol: str) -> Dict[str, Any]:
        """
        获取现货行情信息
        
        Args:
            symbol: 交易对
            
        Returns:
            现货行情信息
        """
        price = self.get_price(symbol)
        return {
            'symbol': symbol,
            'last_price': price,
            'price': price,
            'bid': price * 0.999,
            'ask': price * 1.001,
            'volume': 1000.0,
            'timestamp': int(datetime.now().timestamp() * 1000)
        }
    
    def get_futures_ticker(self, symbol: str) -> Dict[str, Any]:
        """
        获取合约行情信息
        
        Args:
            symbol: 交易对
            
        Returns:
            合约行情信息
        """
        price = self.get_price(symbol)
        return {
            'symbol': symbol,
            'last_price': price,
            'price': price,
            'bid': price * 0.999,
            'ask': price * 1.001,
            'volume': 1000.0,
            'open_interest': 5000.0,  # 持仓量
            'timestamp': int(datetime.now().timestamp() * 1000)
        }
    
    def get_balance(self, asset: str = None) -> Dict[str, float]:
        """
        获取余额
        
        Args:
            asset: 资产名称，为None时返回所有余额
            
        Returns:
            余额信息
        """
        if asset:
            return {asset: self.balances.get(asset, 0.0)}
        return self.balances.copy()
    
    def place_order(self, symbol: str, side: str, order_type: str, 
                   quantity: float, price: Optional[float] = None, 
                   **kwargs) -> str:
        """
        下单
        
        Args:
            symbol: 交易对
            side: 买卖方向
            order_type: 订单类型
            quantity: 数量
            price: 价格
            
        Returns:
            订单ID
        """
        order = self.spot_place_order(symbol, order_type, side, quantity, price)
        return order['order_id']
    
    def get_order(self, order_id: str) -> Optional[Dict[str, Any]]:
        """
        获取订单信息
        
        Args:
            order_id: 订单ID
            
        Returns:
            订单信息
        """
        return self.orders.get(order_id)
    
    def get_open_orders(self, symbol: str = None) -> List[Dict[str, Any]]:
        """
        获取未成交订单
        
        Args:
            symbol: 交易对
            
        Returns:
            订单列表
        """
        orders = []
        for order in self.orders.values():
            if order['status'] in ['new', 'partially_filled']:
                if symbol is None or order['symbol'] == symbol:
                    orders.append(order)
        return orders
    
    def _fill_order(self, order_id: str, fill_price: float, 
                   fill_quantity: Optional[float] = None) -> bool:
        """
        撮合订单，使用改进的价格模拟机制
        
        Args:
            order_id: 订单ID
            fill_price: 成交价格
            fill_quantity: 成交数量
            
        Returns:
            是否成交成功
        """
        if order_id not in self.orders:
            self.logger.warning(f"订单不存在: {order_id}")
            return False
        
        order = self.orders[order_id]
        symbol = order['symbol']
        side = order['side']
        quantity = fill_quantity or order['amount']
        
        # 使用市场价格计算实际成交价格
        market_price = self.get_market_price(symbol, side, quantity)
        if market_price <= 0:
            market_price = fill_price
        
        # 检查余额是否足够
        if not self._check_balance_sufficient(order, quantity, market_price):
            self.logger.warning(f"余额不足，无法成交订单: {order_id}")
            order['status'] = 'rejected'
            return False
        
        # 更新订单状态
        order['status'] = 'filled'
        order['filled_quantity'] = quantity
        order['average_price'] = market_price
        order['fill_time'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        
        # 更新余额
        self._update_balance_after_trade(order, quantity, market_price)
        
        # 记录交易历史
        trade = {
            'trade_id': f"trade_{len(self.trade_history) + 1}",
            'order_id': order_id,
            'symbol': symbol,
            'side': side,
            'quantity': quantity,
            'price': market_price,
            'fee': quantity * market_price * self.fee_rate,
            'timestamp': datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        }
        self.trade_history.append(trade)
        
        self.logger.info(f"订单成交: {order_id}, 价格: {market_price}, 数量: {quantity}")
        return True
    
    def _check_balance_sufficient(self, order: Dict[str, Any], quantity: float, price: float) -> bool:
        """
        检查余额是否足够执行订单
        
        Args:
            order: 订单信息
            quantity: 交易数量
            price: 交易价格
            
        Returns:
            余额是否足够
        """
        symbol = order['symbol']
        side = order['side']
        
        # 解析交易对
        if '/' in symbol:
            base_asset, quote_asset = symbol.split('/')
        else:
            # 假设是BTCUSDT格式
            base_asset = symbol[:-4]
            quote_asset = 'USDT'
        
        if side.lower() == 'buy':
            # 买入需要足够的计价货币
            required_amount = quantity * price * (1 + self.fee_rate)
            available_balance = self.balances.get(quote_asset, 0.0)
            return available_balance >= required_amount
        else:
            # 卖出需要足够的基础货币
            required_amount = quantity
            available_balance = self.balances.get(base_asset, 0.0)
            return available_balance >= required_amount
    
    def get_trade_history(self, symbol: str = None, limit: int = 100) -> List[Dict[str, Any]]:
        """
        获取交易历史
        
        Args:
            symbol: 交易对
            limit: 限制数量
            
        Returns:
            交易历史
        """
        trades = self.trade_history
        if symbol:
            trades = [trade for trade in trades if trade['symbol'] == symbol]
        
        return trades[-limit:] if limit else trades
    
    def _update_balance_after_trade(self, order: Dict[str, Any], 
                                   quantity: float, price: float) -> None:
        """
        交易后更新余额，使用改进的计算逻辑
        
        Args:
            order: 订单信息
            quantity: 成交数量
            price: 成交价格
        """
        symbol = order['symbol']
        side = order['side']
        
        # 解析交易对
        if '/' in symbol:
            base_asset, quote_asset = symbol.split('/')
        else:
            # 假设是BTCUSDT格式
            if symbol.endswith('USDT'):
                base_asset = symbol[:-4]
                quote_asset = 'USDT'
            else:
                base_asset = symbol[:3]
                quote_asset = symbol[3:]
        
        # 确保资产存在
        if base_asset not in self.balances:
            self.balances[base_asset] = 0.0
        if quote_asset not in self.balances:
            self.balances[quote_asset] = 0.0
        
        # 计算手续费
        fee = quantity * price * self.fee_rate
        
        if side.lower() == 'buy':
            # 买入：减少计价货币，增加基础货币
            cost = quantity * price + fee
            self.balances[quote_asset] -= cost
            self.balances[base_asset] += quantity
            
            self.logger.debug(f"买入成交: -{cost:.6f} {quote_asset}, +{quantity:.6f} {base_asset}, 手续费: {fee:.6f}")
        else:
            # 卖出：减少基础货币，增加计价货币
            revenue = quantity * price - fee
            self.balances[base_asset] -= quantity
            self.balances[quote_asset] += revenue
            
            self.logger.debug(f"卖出成交: -{quantity:.6f} {base_asset}, +{revenue:.6f} {quote_asset}, 手续费: {fee:.6f}")
        
        # 确保余额不为负数（防止浮点数精度问题）
        for asset in self.balances:
            if self.balances[asset] < 0 and abs(self.balances[asset]) < 1e-8:
                self.balances[asset] = 0.0